package com.nulldev.util.web.nTube.pages.invidious_compat;

import static com.nulldev.util.web.nTube.pages.Urlizer.urlizer;

import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import com.nulldev.util.VariableAPI.MathUtil;
import com.nulldev.util.VariableAPI.StringsUtil;
import com.nulldev.util.concurrency.threadIt.v4.emapi.ExecutorManager;
import com.nulldev.util.concurrency.threadIt.v4.emapi.uft.Executable;
import com.nulldev.util.data.variable;
import com.nulldev.util.data.Arrays.maps.FastMaps;
import com.nulldev.util.languages.json.uniapi.UniJsonValue;
import com.nulldev.util.web.HttpServer3.Response;
import com.nulldev.util.web.HttpServer3.pages.v3.HttpPage3;
import com.nulldev.util.web.YouTubeExtractor.YouTubeExtractor;
import com.nulldev.util.web.YouTubeExtractor.YouTubeExtractor.ExtractorType;
import com.nulldev.util.web.YouTubeExtractor.extractors.IYouTubeExtractor;
import com.nulldev.util.web.YouTubeExtractor.extractors.IYouTubeExtractorConstants;
import com.nulldev.util.web.YouTubeExtractor.metadata.YouTubeMetadata;
import com.nulldev.util.web.YouTubeExtractor.metadata.YouTubeMetadata.ThumbnailSizeOption;
import com.nulldev.util.web.YouTubeExtractor.related.RelatedParser;
import com.nulldev.util.web.YouTubeExtractor.related.RelatedParser.RelatedInfo;
import com.nulldev.util.web.YouTubeExtractor.related.VideoInfo;
import com.nulldev.util.web.YouTubeExtractor.video.ExtractedVideo;
import com.nulldev.util.web.YouTubeExtractor.video.YouTubeVideo;
import com.nulldev.util.web.nTube.pages.misc.DJSCache;
import com.nulldev.util.web.nTube.util.DashMPDGenerator2;
import com.nulldev.util.web.nTube.util.NTubeUtils;

public class APIVideos extends HttpPage3 {

	public APIVideos() {
		super("/api/v1/videos/");
		this.setCachingMethod(CachingMethod.NO_CACHE);
		this.setChecks(EnhancedSecurityChecks.DISALLOW_POST_DATA, EnhancedSecurityChecks.ONLY_ALLOW_BASIC_METHODS);
	}

	private static final String SEGMENT = "api/v1/videos/";

	@SuppressWarnings("resource")
	@Override
	public Response onRequest(final Request request) {
		final String videoID;
		try {
			videoID = request.url().substring(request.url().indexOf(SEGMENT) + SEGMENT.length());
		} catch (IndexOutOfBoundsException ex) {
			final UniJsonValue v = UniJsonValue.object(2);
			v.add("error", true);
			v.add("error_message", "No videoID provided!");
			return new Response(404, v, v).setCustomMime("application/json");
		}
		final IYouTubeExtractor ext = YouTubeExtractor.get(ExtractorType.PAGE);
		final String _intUrl;
		final String _hdr = request.header("Host", "");
		if (!StringsUtil.isZeroLengthOrNull(_hdr)) {
			if (NTubeUtils.wasOriginHTTPS(request)) {
				_intUrl = "https://" + _hdr;
			} else {
				_intUrl = "http://" + _hdr;
			}
		} else {
			_intUrl = _hdr;
		}
		final Map<String, Object> map = FastMaps.uniMap(new Object[][]
			{
					{ IYouTubeExtractorConstants.MAP_TYPE_VIDEO_ID, videoID },
					{ IYouTubeExtractorConstants.MAP_TYPE_HEATMAP_DISABLE_PRIORITY_POINT, true } });
		final Executable<ExtractedVideo> task = ExecutorManager.get().asyncSubmit(ext.generateTask(map));
		final ExtractedVideo result;
		try {
			result = task.waitFor(30, TimeUnit.SECONDS);
		} catch (Throwable ex) {
			final UniJsonValue v = UniJsonValue.object(2);
			v.add("error", true);
			v.add("error_message", "Extractor task timed out...");
			return new Response(503, v, v).setCustomMime("application/json");
		}
		if (!result.getResult().successful()) {
			final UniJsonValue v = UniJsonValue.object(3);
			v.add("error", true);
			v.add("error_message", "Failed to extract.");
			v.add("error_result", result.getResult().toString());
			return new Response(503, v, v).setCustomMime("application/json");
		} else {
			final UniJsonValue v = UniJsonValue.object();
			final YouTubeMetadata metadata = result.getMetadata();
			v.add("title", metadata.getTitle());
			v.add("videoId", videoID);
			v.add("description", metadata.getDescription());
			v.add("descriptionHtml", metadata.getEnhancedDescription());
			v.add("published", -1L);
			v.add("publishedText", "N/A");
			final UniJsonValue keywords = UniJsonValue.array();
			keywords.asArray().copy(metadata.getTags());
			v.add("keywords", keywords);
			v.add("viewCount", metadata.getViewCount());
			v.add("likeCount", MathUtil.autoConvertToInt(metadata.getLikeCount()));
			v.add("dislikeCount", MathUtil.autoConvertToInt(metadata.getDislikeCount()));
			v.add("paid", false);
			v.add("premium", false);
			v.add("isFamilyFriendly", metadata.getExtendedMetadata().isFamilyFriendly());
			// TODO: Implement the list of regions that YouTube uses.
			v.add("allowedRegions", UniJsonValue.emptyArray());
			v.add("genre", "N/A");
			v.add("genreUrl", "");
			v.add("author", metadata.getAuthor());
			v.add("authorId", metadata.getAuthorID());
			v.add("authorUrl", "");
			v.add("authorThumbnails", UniJsonValue.emptyArray());
			v.add("subCountText", "");
			v.add("lengthSeconds", MathUtil.autoConvertToInt(metadata.getDuration()));
			v.add("allowRatings", false);
			v.add("rating", (float) metadata.getRating());
			v.add("isListed", true);
			v.add("liveNow", metadata.isLiveStream());
			v.add("isUpcoming", metadata.hasContentStartTime());
			if (metadata.hasContentStartTime()) {
				v.add("premiereTimestamp", metadata.getContentStartTime());
			}
			// "hlsUrl": String?,
			if (!result.getVideos().isEmpty()) {
				final String dashString = DashMPDGenerator2.make(result.getVideos(), request, metadata, true, false, false, variable.makeLocked(true), true,
						false, false, false, false);
				final String dashUrl = _intUrl + "/b64_dec?td=" + DJSCache.allocateCache(dashString) + "&cached";
				v.add("dashUrl", dashUrl);
			}
			final UniJsonValue adaptiveFormats = UniJsonValue.array();
			for (final YouTubeVideo video : result.getVideos()) {
				if (video == null)
					continue;
				if (!video.requiresSupplimentaryVideoAudio())
					continue;
				final UniJsonValue obj = UniJsonValue.object(13);
				obj.add("index", video.getIndexPayload());
				obj.add("bitrate", String.valueOf(video.getBitrate()));
				obj.add("init", video.getInitPayload());
				obj.add("url", _intUrl + urlizer(video.videoURL()));
				obj.add("itag", String.valueOf(video.getTag()));
				obj.add("type", video.getCodecMime());
				obj.add("clen", String.valueOf(video.getFileSize()));
				obj.add("lmt", System.currentTimeMillis());
				obj.add("projectionType", "RECTANGULAR");
				obj.add("container", video.getFormat().getFileType().getShortName());
				obj.add("encoding", video.getPrettyMime());
				obj.add("resolution", video.getFormat().getHeight() + "p");
				obj.add("qualityLabel", video.getFormat().getHeight() + "p");
				adaptiveFormats.add(obj);
			}
			v.add("adaptiveFormats", adaptiveFormats);
			final UniJsonValue formatStreams = UniJsonValue.array();
			for (final YouTubeVideo video : result.getVideos()) {
				if (video == null)
					continue;
				if (video.requiresSupplimentaryVideoAudio())
					continue;
				final UniJsonValue obj = UniJsonValue.object(8);
				obj.add("url", _intUrl + urlizer(video.videoURL()));
				obj.add("itag", String.valueOf(video.getTag()));
				obj.add("type", video.getCodecMime());
				obj.add("container", video.getFormat().getFileType().getShortName());
				obj.add("encoding", video.getPrettyMime());
				obj.add("resolution", video.getFormat().getHeight() + "p");
				obj.add("qualityLabel", video.getFormat().getHeight() + "p");
				obj.add("size", video.getFormat().getWidth() + "x" + video.getFormat().getHeight());
				formatStreams.add(obj);
			}
			v.add("formatStreams", formatStreams);
			v.add("captions", UniJsonValue.emptyArray());
			final UniJsonValue recommendedVideos = UniJsonValue.array();
			if (metadata.hasPageData() && !metadata.hasRelatedVideoOverride()) {
				final RelatedInfo vinfo = RelatedParser.parse(metadata.getPageData(), videoID);
				__add_video_info_related(vinfo.related(), recommendedVideos);
			} else if (metadata.hasRelatedVideoOverride()) {
				final List<VideoInfo> vinfo = metadata.getRelatedVideoOverrides();
				__add_video_info_related(vinfo, recommendedVideos);
			}
			v.add("recommendedVideos", recommendedVideos);
			v.add("storyboards", UniJsonValue.emptyArray());
			final UniJsonValue videoThumbnails = UniJsonValue.array();
			if (metadata.hasThumbnailLevel()) {
				for (int i = 0; i < metadata.getThumbnailLevel(); i++) {
					final UniJsonValue entry = UniJsonValue.object(4);
					final ThumbnailSizeOption sizeOption = ThumbnailSizeOption.fromLevel(i);
					final String url = metadata.getThumbnail(sizeOption);
					entry.add("url", url);
					entry.add("quality", sizeOption.name());
					entry.add("width", sizeOption.width());
					entry.add("height", sizeOption.height());
					videoThumbnails.add(entry);
				}
			} else {
				for (int i = 0; i < ThumbnailSizeOption.MAX_RES_DEFAULT_WEBP.getLevel(); i++) {
					final UniJsonValue entry = UniJsonValue.object(4);
					final ThumbnailSizeOption sizeOption = ThumbnailSizeOption.fromLevel(i);
					final String url = metadata.getThumbnail(sizeOption);
					entry.add("url", url);
					entry.add("quality", sizeOption.name());
					entry.add("width", sizeOption.width());
					entry.add("height", sizeOption.height());
					videoThumbnails.add(entry);
				}
			}
			v.add("videoThumbnails", videoThumbnails);
			return new Response(200, v, v).setCustomMime("application/json");
		}
	}

	private void __add_video_info_related(final List<VideoInfo> list, final UniJsonValue recommendedVideos) {
		for (final VideoInfo video : list) {
			final UniJsonValue v = UniJsonValue.object(6);
			v.add("videoId", video.getID());
			v.add("title", video.getName());
			v.add("author", video.getName());
			v.add("lengthSeconds", video.getDuration());
			v.add("viewCountText", String.valueOf(video.getViewCount()));
			final UniJsonValue videoThumbnails = UniJsonValue.array();
			for (int i = 0; i < ThumbnailSizeOption.MAX_RES_DEFAULT_WEBP.getLevel(); i++) {
				final UniJsonValue tee = UniJsonValue.object(4);
				final ThumbnailSizeOption sizeOption = ThumbnailSizeOption.fromLevel(i);
				final String url = YouTubeMetadata.ThumbnailSizeOption.getThumbnail(video.getOnlyID(), sizeOption);
				tee.add("url", url);
				tee.add("quality", sizeOption.name());
				tee.add("width", sizeOption.width());
				tee.add("height", sizeOption.height());
				videoThumbnails.add(tee);
			}
			v.add("videoThumbnails", videoThumbnails);
			recommendedVideos.add(v);
		}
	}
}
